//
// Created by ole on 24-8-21.
//

#include "PluginManager.h"

#include "PluginWrapper.h"
#include "TimerPlugin.h"
#include "../../utils/Log.h"

PluginManager &PluginManager::instance() {
    static PluginManager sInstance;
    return sInstance;
}

void PluginManager::initialize() {
    if (hasStarted) {
        Log::w(TAG, "Already started.");
        return;
    }

    Log::i(TAG, "Initializing.");
    ipcManager.start();
    hasStarted = true;
    loadPlugins();
}

void PluginManager::dispose() {
    if (!hasStarted) {
        Log::w(TAG, "Already stopped.");
        return;
    }

    unloadPlugins();
    hasStarted = false;
    ipcManager.stop();
    Log::i(TAG, "Disposed.");
}

void PluginManager::loopForever() {
    ipcManager.loopForever();
}

void PluginManager::loadPlugins() {
    Log::i(TAG, "Loading plugins.");
    loadCorePlugins();
    loadDynamicPlugins();
}

void PluginManager::loadDynamicPlugins() {
    Log::i(TAG, "Loading dynamic plugins.");
    const std::shared_ptr<PluginWrapper> hotPlugin = std::make_shared<PluginWrapper>("./libPluginHot.so");
    registerPlugin(hotPlugin);
}

void PluginManager::unloadPlugins() {
    Log::i(TAG, "Unloading plugins.");

    std::lock_guard<std::mutex> lock(pluginsMutex);
    for (auto it = plugins.begin(); it != plugins.end();) {
        const std::shared_ptr<PluginWrapper> plugin = it->second;
        plugin->stop();
        it = unregisterPlugin(it);
    }
}

void PluginManager::loadCorePlugins() {
    Log::i(TAG, "Loading core plugins.");
    const std::shared_ptr<BasePlugin> _timerPlugin = std::make_shared<TimerPlugin>();
    const std::shared_ptr<PluginWrapper> timerPlugin = std::make_shared<PluginWrapper>(_timerPlugin);
    registerPlugin(timerPlugin);
}

void PluginManager::registerPlugin(const std::shared_ptr<PluginWrapper> &plugin) {
    plugin->initPlugin(getShared<IPCManager>(getIpcManager()));
    plugin->start();
    std::lock_guard<std::mutex> lock(pluginsMutex);
    plugins[plugin->getName()] = plugin;
    Log::w(TAG, "'" + plugin->getName() + "' registered successfully.");
}

template<typename T>
std::shared_ptr<T> PluginManager::getShared(T &instance) {
    std::shared_ptr<T> manager(&instance, [](const T *pBase) {
        // delete pBase;
    });
    return manager;
}

PluginManager::PluginIterator PluginManager::unregisterPlugin(PluginIterator it) {
    Log::w(TAG, "'" + it->second->getName() + "' unregistered successfully.");
    return plugins.erase(it);
}


std::shared_ptr<BasePlugin> PluginManager::getPlugin(const std::string &name) {
    std::lock_guard<std::mutex> lock(pluginsMutex);
    const std::shared_ptr<PluginWrapper> plugin = plugins[name];
    if (!plugin) {
        Log::w(TAG, "Plugin '" + name + "' not exists.");
    }

    return plugin->getPlugin();
}

IPCManager &PluginManager::getIpcManager() {
    return ipcManager;
}
